package com.study.example.gameserver.module.player.service;

import com.google.common.base.Preconditions;
import com.sparrow.cache.service.CacheService;
import com.sparrow.common.cron.annotation.Cron;
import com.sparrow.common.metric.MetricsManager;
import com.sparrow.common.thread.IdExecutorGroup;
import com.sparrow.common.thread.IoThreadPool;
import com.sparrow.common.thread.executor.EventExecutor;
import com.sparrow.event.api.EventBus;
import com.sparrow.event.annotation.EventReceiver;
import com.sparrow.event.model.Event;
import com.sparrow.example_message.base.resp.RespExpUpdatePacket;
import com.sparrow.example_message.base.resp.RespHpUpdatePacket;
import com.sparrow.resource.cache.ResourceCache;
import com.sparrow.socket.tcp.session.Session;
import com.sparrow.socket.tcp.session.impl.GameSession;
import com.sparrow.socket.tcp.session.listener.SessionListener;
import com.sparrow.socket.tcp.session.manager.GameSessionManager;
import com.study.example.gameserver.base.ConfigService;
import com.study.example.gameserver.module.attr.calculator.impl.LevelAttrCalculator;
import com.study.example.gameserver.module.attr.service.AttrService;
import com.study.example.gameserver.module.player.event.PlayerExpChangeEvent;
import com.study.example.gameserver.module.scene.service.SceneService;
import com.study.example.gameserver.module.player.entity.Player;
import com.study.example.gameserver.module.player.entity.PlayerEntity;
import com.study.example.gameserver.module.player.event.PlayerInitEvent;
import com.study.example.gameserver.module.player.event.PlayerLevelUpEvent;
import com.study.example.gameserver.module.player.event.PlayerLogoutEvent;
import com.study.example.gameserver.module.player.manager.PlayerManager;
import com.study.example.gameserver.module.player.resource.LevelResource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;

@Component
public class PlayerService {

    private static Logger logger = LoggerFactory.getLogger(PlayerService.class);

    @Autowired
    private ConfigService configService;
    @Autowired
    private CacheService cacheService;
    @Autowired
    private IdExecutorGroup idExecutorGroup;
    @Autowired
    private PlayerManager playerManager;
    @Autowired
    private AttrService attrService;
    @Autowired
    private IoThreadPool ioExecutorGroup;
    @Autowired
    private ResourceCache<Integer, LevelResource> levelResCache;
    @Autowired
    private EventBus eventBus;
    @Autowired
    private GameSessionManager sessionManager;
    @Autowired
    private SceneService sceneService;

    @PostConstruct
    public void init() {
        sessionManager.addListener(new SessionListener() {
            @Override
            public void onSessionBind(Session session) {

            }

            @Override
            public void onSessionRemove(Session session) {
                GameSession gameSession = (GameSession) session;
                logout(gameSession);
            }
        });
    }

    @EventReceiver
    public void onPlayerInit(PlayerInitEvent event) {
        Player player = event.getPlayer();
        player.getScheduler().reset();
        player.getScheduler().scheduleAtFixedRate("BuffTick", (p) -> {
            player.getBuffBox().update(System.currentTimeMillis());
        }, 1, 1, TimeUnit.SECONDS);
    }


    private void logout(GameSession gameSession) {
        if (gameSession.getEntity() == null) {
            return;
        }
        Player player = (Player) gameSession.getEntity();
        player.getScheduler().shutdown();
        eventBus.publish(Event.newBuilder(new PlayerLogoutEvent(player)).build());
        logger.info("player:{} logout", player.getPlayerId());
        MetricsManager.counter("LogoutCount").increment();
    }

    public Long getPlayerIdByAccountKey(String accountKey) {
        return playerManager.getPlayerIdByAccountKey(accountKey);
    }

    public boolean containName(String name) {
        return playerManager.containName(name);
    }

    /**
     * 执行玩家异步操作
     * @param playerId
     * @param consumer
     */
    public void execPlayerAsync(long playerId, Consumer<Player> consumer) {
        Player player = playerManager.getPlayer(playerId);
        if (player != null && player.isInitialize()) {
            idExecutorGroup.execute("execPlayerAsync-" + playerId, player.getPlayerId(), () -> {
                consumer.accept(player);
            });
        } else {
            CompletableFuture<Player> future = new CompletableFuture<>();
            ioExecutorGroup.execute("execPlayerAsync-" + playerId, () -> {
                EventExecutor executor = idExecutorGroup.takeExecutor(playerId);
                Player curPlayer = new Player(playerId, executor);
                Player pre = playerManager.getPlayerCache().asMap().put(playerId, curPlayer);
                if (pre != null) {
                    curPlayer = pre;
                }
                if (!curPlayer.isInitialize()) {
                    synchronized (curPlayer) {
                        if (!curPlayer.isInitialize()) {
                            //加载玩家数据
                            playerManager.loadPlayerData(curPlayer);
                            curPlayer.setInitialize(true);
                        }
                    }
                }
                future.complete(curPlayer);
            });
            future.whenCompleteAsync((p,t) -> {
                idExecutorGroup.execute("execPlayerAsyncCallback", p.getPlayerId(), () -> {
                    consumer.accept(p);
                });
            });
        }
    }

    public void addExp(Player player, int addExp) {
        Preconditions.checkState(addExp > 0);
        PlayerEntity playerBaseInfo = player.getEntity();
        playerBaseInfo.setExp(playerBaseInfo.getExp() + addExp);
        int oldLevel = playerBaseInfo.getLevel();
        LevelResource nextLevelRes = levelResCache.getResource(playerBaseInfo.getLevel() + 1);
        while (nextLevelRes != null && nextLevelRes.getNeedExp() > 0 && playerBaseInfo.getExp() > 0 && playerBaseInfo.getExp() >= nextLevelRes.getNeedExp()) {
            playerBaseInfo.setExp(playerBaseInfo.getExp() - nextLevelRes.getNeedExp());
            playerBaseInfo.setLevel(nextLevelRes.getLevel());
            nextLevelRes = levelResCache.getResource(playerBaseInfo.getLevel() + 1);
        }
        if (playerBaseInfo.getLevel() != oldLevel) {
            eventBus.publish(Event.newBuilder(new PlayerLevelUpEvent(player)).dispatchCode(player.getPlayerId()).build());
        }
        playerBaseInfo.update();

        sendExpUpdate(player);

        eventBus.publish(Event.newBuilder(new PlayerExpChangeEvent(player)).dispatchCode(player.getPlayerId()).build());
    }

    public void sendExpUpdate(Player player) {
        RespExpUpdatePacket packet = new RespExpUpdatePacket();
        packet.setExp(player.getExp());
        packet.setLevel(player.getLevel());
        player.sendPacket(packet);
    }

    @Cron(value = "0 * * * * ?")
    public void min() {
        logger.info("在线人数:{}", sessionManager.getAllSession().size());
    }

    public void sendHpInfo(Player player) {
        RespHpUpdatePacket packet = new RespHpUpdatePacket();
        packet.setHp(player.getHp());
        packet.setMaxHp(player.getMaxHp());
        player.sendPacket(packet);
    }

    @EventReceiver
    public void onPlayerLevelUp(PlayerLevelUpEvent event) {
        Player player = event.getPlayer();
        attrService.getAttrCalculator(LevelAttrCalculator.class).calcAttr(player);
        player.setHp(player.getMaxHp());
        player.save();

        sendHpInfo(player);
        logger.info("计算玩家等级属性 player:{} attr:{}", player.getPlayerName(), player.getAttrBox().getFinalAttr().toString());
    }
}
